


#include "arp.h"
#include "net.h"



void arp_table_update(ipaddr_t ip, qe_u8 *ether)
{
    int idx;
    struct net_gd *gd = net_get_gd();
    struct arp_entry *e;

    idx = (gd->arp_entry_num + 1) % ARP_TABLE_SIZE;
    e = &gd->arp_table[idx];
    e->ip = ip;
    qe_memcpy(e->ether, ether, 6);
    gd->arp_entry_num++;
    //net_debug("arp table update %p "MAC_DUMP_FMT, ip, MAC_DUMP_ARRAY(ether));
}

int arp_table_lookup_byipaddr(ipaddr_t ip)
{
    int i;
    struct net_gd *gd = net_get_gd();

    for (i=0; i<ARP_TABLE_SIZE; i++) {
        if (ip == gd->arp_table[i].ip)
            return i;
    }

    return -1;
}

void arp_raw_request(ipaddr_t src_ip, const qe_u8 *target_ether, 
    ipaddr_t target_ip)
{
    qe_u8 *pkt;
    struct arp_hdr *arp;
    int eth_hdr_size;
    struct net_gd *gd = net_get_gd();

    net_debug("ARP broadcast %d", gd->arp_wait_retry);

    pkt = gd->next_arp_tx_packet;

    eth_hdr_size = net_set_ether(pkt, gd->bcastaddr, PROT_ARP);
	pkt += eth_hdr_size;

	arp = (struct arp_hdr *) pkt;

	arp->ar_hrd = qe_htons(ARP_ETHER);
	arp->ar_pro = qe_htons(PROT_IP);
	arp->ar_hln = ARP_HLEN;
	arp->ar_pln = ARP_PLEN;
	arp->ar_op = qe_htons(ARPOP_REQUEST);

	qe_memcpy(&arp->ar_sha, &gd->ipaddr, ARP_HLEN);	/* source ET addr */
	net_write_ip(&arp->ar_spa, src_ip);		/* source IP addr */
	qe_memcpy(&arp->ar_tha, (void *)target_ether, ARP_HLEN);	/* target ET addr */
	net_write_ip(&arp->ar_tpa, target_ip);		/* target IP addr */
    net_debug("send ARP Request");
	//net_send_packet(gd->next_arp_tx_packet, eth_hdr_size + ARP_HDR_SIZE);
}

void arp_request(void)
{
    struct net_gd *gd = net_get_gd();

    gd->arp_wait_replay_ip = gd->arp_wait_packet_ip;

    arp_raw_request(gd->ipaddr, gd->ether_null, gd->arp_wait_replay_ip);
}

void arp_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, int len)
{
    struct arp_hdr *arp;
    ipaddr_t reply_ip_addr;
    qe_u8 *pkt;
    int eth_hdr_size;
    struct net_gd *gd = net_get_gd();

	/*
	 * We have to deal with two types of ARP packets:
	 * - REQUEST packets will be answered by sending  our
	 *   IP address - if we know it.
	 * - REPLY packates are expected only after we asked
	 *   for the TFTP server's or the gateway's ethernet
	 *   address; so if we receive such a packet, we set
	 *   the server ethernet address
	 */


    arp = (struct arp_hdr *)ip;
    if (len < ARP_HDR_SIZE) {
        //net_err("bad len %d < %d", len, ARP_HDR_SIZE);
        return;
    }

    if (qe_htons(arp->ar_hrd) != ARP_ETHER) {
        return;
    }
    if (qe_htons(arp->ar_pro) != PROT_IP)
        return;
    if (arp->ar_hln != ARP_HLEN)
        return;
    if (arp->ar_pln != ARP_PLEN)
        return;
    
    if (gd->ipaddr == 0)
        return;
    
    if (net_read_ip(&arp->ar_tpa) != gd->ipaddr)
        return;
    
    switch (qe_htons(arp->ar_op)) {
    
    case ARPOP_REQUEST:
        /* reply with our IP address */
        //net_debug("ARP Request, return local ip");
        if (arp_table_lookup_byipaddr(ip->ip_src) < 0) {
            arp_table_update(ip->ip_src, et->src);
        }
        pkt = (qe_u8 *)et;
        eth_hdr_size = net_update_ether(et, et->src, PROT_ARP);
        pkt += eth_hdr_size;
        arp->ar_op = qe_htons(ARPOP_REPLY);
        qe_memcpy(&arp->ar_tha, &arp->ar_sha, ARP_HLEN);
        net_copy_ip(&arp->ar_tpa, &arp->ar_spa);
        qe_memcpy(&arp->ar_sha, gd->hwaddr, ARP_HLEN);
        net_copy_ip(&arp->ar_spa, &gd->ipaddr);
        //net_debug("send ARP replay");
        net_send_packet((qe_u8 *)et, eth_hdr_size + ARP_HDR_SIZE);
        return;
    
    case ARPOP_REPLY:		/* arp reply */
        if (!gd->arp_wait_packet_ip)
            return;
        reply_ip_addr = net_read_ip(&arp->ar_spa);

        /* matched waiting packet's address */
        if (reply_ip_addr == gd->arp_wait_packet_ip) {
            //net_debug("ARP Reply, set eth addr (%pM)", arp->ar_data);

            /* save address for later use */
            if (gd->arp_wait_packet_mac != QE_NULL)
                qe_memcpy(gd->arp_wait_packet_mac, &arp->ar_sha, ARP_HLEN);

			/* set the mac address in the waiting packet's header
			   and transmit it */
            qe_memcpy(((struct ethernet_hdr *)gd->next_tx_packet)->dst, 
                &arp->ar_sha, ARP_HLEN);
            //net_debug("send packet");
            net_send_packet(gd->next_tx_packet, gd->arp_wait_tx_packet_size);

            /* no arp request pending now */
            gd->arp_wait_packet_ip = 0;
            gd->arp_wait_tx_packet_size = 0;
            gd->arp_wait_packet_mac = QE_NULL;
        }
        return;

    default:
        //net_debug("unknown ARP opcode 0x%x", qe_htons(arp->ar_op));
        return;
    }
}
